package game;

import java.util.ArrayList;

/**
 * A standard movable object that can be placed inside a game
 * @author Twisty
 */
public class MovableGameObject extends GameObject {
	private int prevX, prevY; //Previous x and y coordinates
	private int moveSpeed = 4; //Movespeed of the object
	private double rotationSpeed = 5; //2 degrees per game tick maximum
	private double desRotation = 0; //Angle where the object is desires to look at.
	
	private double xSpeed, ySpeed; //Horizontal and vertical speed

	/**
	 * Class constructor.
	 * @param x 	X starting coordinate
	 * @param y 	Y starting coordinate
	 */
	public MovableGameObject(int x, int y) {
		super(x, y);
	}

	/**
	 * Get the speed on X axis.
	 * @return The current xSpeed
	 */
	public double getXSpeed(){
		return this.xSpeed;
	}
	
	/**
	 * Get the speed on Y axis.
	 * @return The current ySpeed
	 */
	public double getYSpeed(){
		return this.ySpeed;
	}
	
	/**
	 * Set a new speed on X axis.
	 * @param newXSpeed	The new x speed
	 */
	public void setXSpeed(double newXSpeed){
		this.xSpeed = newXSpeed;
	}
	
	/**
	 * Set a new speed on Y axis.
	 * @param newYSpeed The new y speed
	 */
	public void setYSpeed(double newYSpeed){
		this.ySpeed = newYSpeed;
	}
	
	/**
	 * Set a new movement speed.
	 * @param speed	The new movement speed.
	 */
	public void setMoveSpeed(int moveSpeed){
		this.moveSpeed = moveSpeed;
	}
	
	/**
	 * Set desired rotation angle.
	 * @param rotation	The new rotation.
	 */
	public void setDesRotation(double rotation){
		System.out.println("setDesrotation: "+ rotation);
		this.desRotation = rotation;
	}
	
	/**
	 * Get destionation rotation.
	 * @return The current rotation.
	 */
	public double getDesRotation(){
		return this.desRotation;
	}
	
	/**
	 * Get rotationspeed.
	 * @return The current rotation speed.
	 */
	public double getRotationSpeed(){
		return this.rotationSpeed;
	}
	
	/**
	 * Checks if object collides with map
	 * @param gameMap 	Map of the game
	 * @return 	True if there is a collision with the map.
	 */
	public boolean collidesWithMap(GameMap gameMap){
		boolean collide = false;
		for(int xx = this.getX(); xx<=(this.getX()+getHeight()); xx++){
			for(int yy = this.getY(); yy<=(this.getY()+getWidth()); yy++){
				try{
					if(gameMap.hasPointAtLocation(xx, yy)){
						collide = true;
					}
				}catch(java.lang.ArrayIndexOutOfBoundsException e){
					
				}
			}
		}
		return collide;
	}
	
	/**
	 * Returns an int[2] with the x (int[0]) and y (int[1]) coordinates of the place where the object collides with the map
	 * @param gameMap 	Map of the game
	 * @return int[2] with coordinates x (int[0]) and y (int[1])
	 */
	public int[] collidesWithMapOnPosition(GameMap gameMap){
		//CAN BE IMPROVED BY USING JAVA.AWT.POINT!!
		int[] coordinates = new int[2];
		for(int xx = this.getX(); xx<=(this.getX()+getHeight()); xx++){
			for(int yy = this.getY(); yy<=(this.getY()+getWidth()); yy++){
				try{
					if(gameMap.hasPointAtLocation(xx, yy)){
						coordinates[0] = xx;
						coordinates[1] = yy;
					}
				}catch(java.lang.ArrayIndexOutOfBoundsException e){
					
				}
			}
		}
		return coordinates;
	}
	
	/**
	 * Returns whether the object can move without going out of map bounds
	 * @param mapWidth 	Width of the map
	 * @param mapHeight Height of the map
	 * @return True if player can move (depends on xSpeed & ySpeed)
	 */
	public boolean canMoveWithoutGoingOutOfMap(int mapWidth, int mapHeight){
		boolean canMoveX = false, canMoveY = false;
		if(getXSpeed() == 0 && getYSpeed() == 0){
			return true;
		}
		
		if(getXSpeed() > 0){
			if(this.getX() + this.getWidth() + getXSpeed() < mapWidth){
				canMoveX = true;
			}
		}else if(getXSpeed() < 0){
			if(this.getX() + getXSpeed() > 0){
				canMoveX = true;
			}
		}
		if(getYSpeed() > 0){
			if(this.getY() + this.getHeight() + getYSpeed() < mapHeight){
				canMoveY = true;
			}
		}else if (getYSpeed() < 0){
			if(this.getY() + getYSpeed() > 0){
				canMoveY = true;
			}
		}
		if(canMoveX && canMoveY){
			return true;
		}else{
			return false;
		}
	}
	
	/**
	 * Check for collisions with objects in the given arrayList
	 * @param potentialColliders  Game objects to check collision on.
	 */
	public void collisionCheck(ArrayList potentialColliders){
		ArrayList colliders = this.collidesWithObjects(potentialColliders);
		for(Object o: colliders){
			if(o instanceof GameObject){
				collisionHandler((GameObject)o);
			}
		}
	}
	
	/**
	 * Method that gets called by the collisionCheck. Needs to be overridden by every objects that extends this
	 * @param collider	The GameObject that collides with this object
	 */
	public void collisionHandler(GameObject collider){
		
	}
	
	/**
	 * This function returns a list of game objects that collide with this MovableGameObject
	 * @param potentialColliders	 A list of objects that can collide with this object
	 * @return A list of objects that collide with this object
	 */
	private ArrayList collidesWithObjects(ArrayList potentialColliders){
		ArrayList gameObjects = new ArrayList();
		for(Object g: potentialColliders){
			if(g instanceof GameObject){
				GameObject gg = (GameObject) g;
				if(!gg.equals(this)){
					int newX = (this.getX() + (int) this.getXSpeed());
					int newY = (this.getY() + (int) this.getYSpeed());
					if(this.getXSpeed() > 0){
						newX+=this.getWidth();
					}
					if(this.getYSpeed() > 0){
						newY+= this.getHeight();
					}
					if(newX >= gg.getX() && newX <= gg.getX()+gg.getWidth()){
						if(newY >= gg.getY() && newY <= gg.getY()+gg.getHeight()){
							gameObjects.add(gg);
						}
					}
				}
			}
		}
		return gameObjects;
	}
	
	/**
	 * Moves the object to the current direction
	 */
	public void moveToCurrentDirection(){
		this.xSpeed = (moveSpeed * Math.cos(Math.toRadians(this.getRotation())));
		this.ySpeed = (moveSpeed * Math.sin(Math.toRadians(this.getRotation())));
		this.prevX = this.getX();
		this.prevY = this.getY();
		this.addCoordinates(xSpeed, ySpeed);
	}
	
	/**
	 * The function that will be called by the game every step for each movable object
	 * @param gameMap	The current map of the game
	 */
	public void move(GameMap gameMap){
		this.moveToCurrentDirection();
		if(this.collidesWithMap(gameMap)){
			this.setCoordinates(prevX, prevY);
		}
	}
	
	/**
	 * The step event that check collisions and moves the object every cycle
	 * @param gameMap 	Map of the game
	 * @param potentialColliders ArrayList with potential colliders
	 */
	public void step(GameMap gameMap, ArrayList potentialColliders){
		this.move(gameMap);
		this.collisionCheck(potentialColliders);
	}
}
